<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://www.netvibes.com/ns/" >

<!-- start header-->
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<title>Twitter King Widget dev</title>
	<meta name="author" content="Georg Leciejewski" />
	<meta name="description" content="A Widget to interact with the tiny great twitter world." />
	<meta name="apiVersion " content="1.0" />
	<meta name="inline" content="true" />
	<meta name="autoRefresh " content="1" />
	<meta name="debugMode" content="true" />

	<link rel="icon" type="image/png" href="http://www.twitter.com/favicon.ico" />
<!-- end header -->

<!-- start settings -->
	<widget:preferences>
		<preference name="username" type="text" label="Your Username" />
		<preference name="pass" type="password" label="Twitter Password" />
		<preference name="buddy" type="text" label="Buddy to watch (Public)" />
        <preference name="limit" type="range" label="Limit Messages"
			defaultValue="10" step="1" min="1" max="20" />
		<preference name="refresh_time" type="range" label="Refresh every sec."
			defaultValue="60" step="10" min="10" max="100" />

        <preference name="timeline_type" type="list" label="Timeline Type" onchange="refresh">
			<option value="my_user" label="My Timeline" />
			<option value="friends" label="My Friends Timeline" />
			<option value="direct_messages" label="My Direct Messages" />
			<option value="buddy" label="Buddys Timeline" />
			<option value="buddy_friends" label="Buddys Friends Timeline" />
			<option value="public" label="Global Public" />
		</preference>
		<preference name="sound" type="list" label="Choose your sound" onchange="refresh">
			<option value="no_sound" label="I hate soundz" />
			<option value="twitter" label="George trys to Twitter" />
		<!--	<option value="cat" label="Miau the cat" />
			<option value="pig" label="Grunt the piglet" />
			<option value="dog" label="Wau the dog" />-->
		</preference>
	<preference name="search" type="hidden" defaultValue="" />
	<preference name="version" type="hidden" defaultValue="beta 0.85" />

	</widget:preferences>
<!-- end settings -->

<!-- start controller-->

<script type="text/javascript" src="http://www.netvibes.com/js/UWA/load.js.php?env=Standalone"></script>

<script type="text/javascript">

/**
 * The Twitter King  Widget
 * it once was a king, in a land far from here, who wasn�t quite up to date about his friends
 * All his herolds had quitted their job, because of the great dangers they encounterd when delivering their messages.
 *
 *TO Do
 - implement /destroy friendship
 - implement @username listings
 - implement longer listings &page=2
 - implement message deletion
 - implement update checker
 - button to refresh user list
 Done
 - create friendship with followers
 */
var TK = {

/************************************************
 * The database or temporary brain
 *************************************************/
	db:{

        /**
		* my friends are saved here and can be used for dropdowns and other funky stuff
		*/
		friends:{},
		/**
		* holding all ids of my friend users to quickly lookup
		* needed to decide wether to show different links in the persons lists 
		* followers get different links 
		*/
		friends_ids:[],
        /**
		* my followers if i'm a spicy Queen
		*/
		followers:{},
		/**
		* when requesting the details/followers/friends of me or another person
		* this is used as stack to trace the route and show the names
		* @
		*/
		lookedUpPersons:[],
		/**
		* available soundz
		*/
		soundz:{
			dog: ['dog_1.mp3','dog_2.mp3','dog_3.mp3'],
			cat: ['dog_1.mp3','dog_2.mp3','dog_3.mp3'],
			twitter: ['twitter_1.mp3','twitter_2.mp3','twitter_3.mp3','twitter_4.mp3']
		},
		/**
		*
		*/
		tabs:{},
		/**
		* timeline/message cache
		* msgType:id
		*/
		lastMessage:{},

		/**
		* define the key/value pairs for the dropdown tabs
		* those are used to get/set the active dropdown states
		*/
		dropTabs:{
			/**
			* the tab names + values of the first tab
			* is used f.ex.: lookup the name in the name array, take its array index no.
			*				and get the value with this index no. from the type array.
			* this way there is no need for long switch or if/else statements
			* and the names can be easily changed later in this place
			*/
			tab10:{
				//name: [{text:'Me'},{text:'My Friends'},{text:'Direct Msg'},{text:'My Buddy'},{text:'Buddys Friends'},{text:'Global Public'}],
				name: ['Me','My Friends','Direct Msg','My Buddy','Buddys Friends','Global Public'],
				type: ['my_user','friends','direct_messages','buddy','buddy_friends','public']
			}
		}	
		

	},
/************************************************
 * The Model
 * data handling like transfer / CRUD Operations
 *************************************************/
	model:{
		/**
		 * make the request to twitter, used by findFriends/findFollowers
		 * @param {String} sub_url 		- the substring of the restfull twitter url 
		 * @param {Function} callback	- the function to return to with the data
		 * @param {String} options		- additonal options like number of items to return
		 */
		makeGetRequest: function(sub_url, callback, options){
			widget.log('Start: model.makeGetRequest');
			var base_url= 'http://twitter.com';
			!(options) ? options='' : options;
			var url		= base_url + sub_url + '.json'+options;

			UWA.Data.request(
				url,
				{	method: 'get',
					type: 'json',
					onComplete: callback,
					authentication: TK.model.getAuth()
				}
			);
			return false;
		},
        /**
		 * save the friends list so it can be accessed quickly later on
		 * the ids are saved into a seperate db array, used to decide if someone
		 * out of a user list is already my friend or gets some extra links
		 * @param {Object} data
		 */
		saveFriends: function(data){
			widget.log('Start: model.saveFriends');
			TK.db.friends = data;

			for(var i =0; i < data.length; i++)
			{//push user ids onto db stack
				TK.db.friends_ids.push(data[i].id);
			}
		},
		/**
		 * save the followers list so it can be accessed quickly later on
		 * @param {Object} data
		 */
		saveFollowers: function(data){
			widget.log('Start: model.saveFollowers');
			TK.db.followers = data;
			
		},
		/**
		 * submit a new message to twitter
		 * the url comes from the forms action
		 * or is set if its a private message
		 * @return boolean false
		 */
		submitStatus: function (div) {
			widget.log('Start: model.submitStatus');
			//widget.log(div);			
			
			var parameters	= '';
			var status		= div.getElement('textarea').getValue();
			var user 		= div.getElement('select').getValue();
			var callback 	= function (data){TK.controller.afterSubmit(data, div);};
			
			if(user !="")
			{//if private message user not empty send as private message
				url = 'http://twitter.com/direct_messages/new.json';
				parameters ='user='+user+'&text=' +  status;
				
			}
			else
			{//normal timeline msg submit				
				url = div.getElement('form').action;
				parameters ='status=' +  status;			
			}

			UWA.Data.request(
				url,
				{ 	method: 'post',
					proxy: 'ajax',
					onComplete: callback,
					parameters: parameters,
					authentication: TK.model.getAuth()
				}
			);

    		div.getElement('textarea').setAttribute('disabled', 'disabled');
    		return false;
		},
        /**
		 * get informations about a single user
		 * @param {Function} callback - the callback function to return to with the data after the post
		 * @param {String} person - when set tries to send a private msg to this user -> nickname or id
		 */
		findDetails: function (callback, person){
			//should maybe have a callback in the controller

			var url = '';
			if(person != undefined)
			{// lookup informations about this special person
				 url = '/users/show/'+person;
			}
			else
			{// look me up
				 url = '/users/show/'+widget.getValue('username');
			}

			TK.model.makeGetRequest(url, callback);
		},
		/**
		 * find the followers of the authenticated user
		 * @param {Function} callback - the callback function to return to with the data after the get
		 * @param {String} person - when set gets followers from this user -> nickname or id
		*/
		findFollowers: function (callback, person){
			//should maybe have a callback in the controller
			widget.log('Start: model.findFollowers');
	
            var url = '';

			if(typeof person == 'string')
			{//the followers of my friends
				url = '/statuses/followers/'+person;
			}
			else
			{//get my followers
				url = '/statuses/followers';
			}		

			if(callback != undefined)
			{//special callback given
				TK.model.makeGetRequest(url, callback);
			}
			else
			{//save in db object prison
				TK.model.makeGetRequest(url, TK.model.saveFollowers);
			}
			return false;
		},
		
		/**
		 * create a friendship with this person
		 * actually meaning you are starting to follow him .. i gues .. twitter changed its naming
		 * @param {Function} callback - the callback function to return to with the data after the post
		 * @param {Int} person 		- id of the user to add as friend
		 */
		addFriend: function (callback, person_id) {
			var url = '/friendships/create/'+person_id;
			TK.model.makeGetRequest(url, callback);
		},
		/**
		 * stop being a friend this person - stop following him
		 * @param {Function} callback
		 * @param {Int} person_id
		 */
		destroyFriend: function (callback, person) {
			var url = '/friendships/destroy/'+person_id;
			TK.model.makeGetRequest(url, callback);
		},
		
		/**
		 * find the friends of the authenticated user
		 * and save them in the virtual database
		 * @param callback 	- the callback invoked after the find
		 * @param person	- the person optional, if empty returns my friends
		 */
		findFriends: function (callback,person){
			widget.log('Start: model.findFriends');
			var url = ''
			//later implement: http://twitter.com/statuses/friends/bob.rss to get friends of that user

			if(person != undefined)
			{//the friend of my friend once was bitten by a spider ... so look him up
				url = '/statuses/friends/'+person;
			}
			else
			{//get my friends
				url = '/statuses/friends';
			}
		
			//need to check if we still need it
			if(callback != undefined)
			{
				TK.model.makeGetRequest(url, callback);
			}
			else
			{//save in db
				TK.model.makeGetRequest(url, TK.model.saveFriends);
			}
			return false;
		},
		/**
		 * find different kinds of timelines depending
		 * on the widget settings or the choosen tab dropdown
		 */
		findTimeline: function (tab_key_name) {
			widget.log('Start: model.findTimeline');

			var user	= widget.getValue('username');
			var buddy	= widget.getValue('buddy');
			var count 	= widget.getValue('limit');
			var error	= false;
			var auth	= false;
			var type 	= '';
			var url		= '';
			var error_msg = '';
			var index	 = 0;

			if(tab_key_name != undefined)
			{// get the array index by type to get the nice name by this id
				ar_index = TK.db.dropTabs.tab10.name.indexOf(tab_key_name);
				type 	 = TK.db.dropTabs.tab10.type[ar_index];
				//save the choosen timeline in settings
				widget.setValue('timeline_type',type);
			}
			else
			{//no type given so take default from settings
				type = widget.getValue('timeline_type');
			}


			switch(type)
			{// change the request url according to the timeline type
				case 'my_user':
					if(user == null){// no username set
						error_msg = 'How shall I display your timeline without knowing your Username? Haven´t we been throught this before?';
						error = true;
					}else{
						url = '/statuses/user_timeline';
						auth = true;
						widget.setTitle(user + ' twitters');
					}
					break;
				case 'friends':
					if(user == null){
						error_msg = 'How shall I display your friends timeline without knowing your name? Moo Moo buckaroo...';
						error = true;
					}else{
						url = '/statuses/'+type+'_timeline/'+user;
						widget.setTitle(user + '\'s friends twitter');
						auth = true;
					}
					break;
				case 'direct_messages':
					if(user == null){// no username set
						error_msg = 'How shall I display your direct messages without knowing your Username? Ohh dude...you are not Web 2.0';
						error = true;
					}else{
						url = '/direct_messages';
						auth = true;
						widget.setTitle('Direct Messages inbox');
					}
					break;
				case 'buddy':
					if(buddy == null || buddy ==''){
						error_msg = 'How shall I display your buddys timeline without knowing his name? Ha...';
						error = true;
					}else{
						url = '/statuses/user_timeline/'+buddy;
						widget.setTitle(buddy + ' twitters');
					}
					break;
				case 'buddy_friends':
					if(buddy == null || buddy ==''){
						error_msg = 'How shall I display your buddys friends timeline without knowing his name? Come on ... its not that hard';
						error = true;
					}else{
						url = '/statuses/friends_timeline/'+buddy;
						widget.setTitle(buddy + '\'s friends twitter');
					}
					break;
				case 'public':
					url = '/statuses/public_timeline';
					widget.setTitle('Main Public timeline');
					break;
				default:
					url = '/statuses/public_timeline';
					widget.setTitle('Main Public timeline');
					//still need to set the tabs dropdown value too
					break;
				}

				var options = '?count='+count;

				if(error == false)
				{//no error occured make request
					!(options) ? options='' : options;
					var url	= 'http://twitter.com' + url + '.json'+options;

					UWA.Data.request(url,
									{	method: 'get',
										type: 'json',
										onComplete: function(data){ TK.controller.afterFindTimeline(data,type);},
										authentication: TK.model.getAuth()
									});
				}
				else
				{//show the error
					TK.view.showError(error_msg, 'tab10');
				}
		},

		/**
		* check for new messages, only get last one
		* if new is found (by matching with db.lastMessages)
		* the timeline type is again fetched
		*/
		findUpdate: function (type) {
            var url	= 'http://twitter.com' + url + '.json?count='+count;

			UWA.Data.request(url,
							{	method: 'get',
								type: 'json',
								onComplete: function(data){ TK.controller.afterFindUpdate(data,type);},
								authentication: TK.model.getAuth()
							});
		},
		
		/**
		* return a random sound filename depending on the widget settings
		*/
		getSound: function () {
			var sound_type = widget.getValue('sound');
			var sound = new Array();
			switch(sound_type){
                case 'no_sound':
					return false;
					break;
				case 'dog':
					sound = TK.db.soundz.dog;
					break;
                case 'twitter':
					sound = TK.db.soundz.twitter;
					break;
                case 'pig':
                	sound = TK.db.soundz.pig;
					break;
				case 'cat':
					sound = TK.db.soundz.cat;
					break;
			}			
			return sound.getRandom();
		},

		/**
		 * Authenticate the user
		 * keept seperate to allow better handling between standalone and inline netvibes mode
		 * currently disabled in calling function makeGetRequest, because of some probs
		 */
		getAuth: function () {

			widget.log('Start: model.getAuth');
			var authentication ={
								moduleId: widget.id,
								username: widget.getValue('username'),
								type: 'http_basic'
								};

			if (widget.data.pass)
			{// enable authentication outside of Netvibes
				authentication.password = widget.data.pass;
			}
			return authentication;
		}
	 
	}, // end model

/***************************************************
 * The Controller
 * Interaction with the user interface
 * - events/tab change/observers
 * - pass model data throught to the views
 ****************************************************/
	controller: {

		/**
		 * index function initializing the tabs and their contents
		 * @param {string} data - the error message
		 */
		index: function () {
			widget.log('Start: controller.index');

			var tabs = new UWA.Controls.TabView();
			TK.db.tabs = tabs;

			// Create tabs
			tabs.addTab("tab10",TK.controller._setTabDropdownText('tab10'));
			tabs.addTab("tab20", {text: "Write"} );
			tabs.addTab("tab30", {text: "Friends"});
			tabs.addTab("tab40", {text: "Followers"});
			tabs.addTab("tab50", {text: "Me"});
			tabs.addTab("tab60", {text: "Help"});

			//set inital content
			tabs.setContent("tab10",'Easy Babe ... i am already asking Twitter for your Stuff.');
			tabs.setContent("tab20",'Message Form comes in here.');
			tabs.setContent("tab30", 'Shows your Friends.');
			tabs.setContent("tab40",'Show your Followers.');
			tabs.setContent("tab50", 'Show your Twitter Account Informations');
			tabs.setContent("tab60", 'Help');

			// Restore saved active tab
			var activeTab = widget.getValue("activeTab");

			if (activeTab && activeTab !='tab1'){ // must exclude tab1 cause uwa sets this as default in standalone
				tabs.selectTab(activeTab);
			}
			// Register to activeTabChange event
			tabs.observe('activeTabChange', TK.controller.onActiveTabChange);

			//shave that hot body
			widget.setBody('');
			// Append tabs to widget body
			tabs.appendTo(widget.body);

            if((widget.inline === false) && (null == widget.getValue('pass')) )
			{// the password field is not saved when not in inline mode on netvibes
				tabs.setContent("tab10", '<div class="error">Please gimme your password again. Sorry to bug...</div>');
			}
			else
			{//get content for first tab
                TK.model.findTimeline();
				//find friends and save them for later use
				TK.model.findFriends();
				TK.model.findFollowers();
			}
			TK.controller._standaloneCompat();
		},

        /**
		* show my friends. calls the model with the view as a callback
		* trys to free my friends from the TK.db prison,
		* if they are not in there ask twitter again
		*/
		showFriends: function (person) {
			widget.log('Start controller.showFriends');
			
			if(typeof person == 'string')
			{//lookup the friends of a friend
				TK.db.tabs.selectTab('tab30');
				//put that girl in the stack prison
				TK.db.lookedUpPersons.push(person);
				//data is not cached like my friends are
				TK.model.findFriends(TK.view.showFriends,person);
			}
			else
			{			
				if(typeof TK.db.friends[0] == 'object')
				{//get friends from db
					TK.view.showFriends(TK.db.friends);
				}
				else
				{//get them from twitter .. should actually never occur
					TK.model.findFriends(TK.view.showFriends);
				}
			}
		},
		/**
		* show the kings followers listing.
		* calls the models find function if person is given or db cache is empty
		* or renders the view if he finds cached followers
		*/
		showFollowers: function(person){
            widget.log('Start: controller.showFollowers');

            if(typeof person == 'string')
			{//bringing in some new persons
				TK.db.tabs.selectTab('tab40');
                //put that girl in the stack prison
				TK.db.lookedUpPersons.push(person);
				TK.model.findFollowers(TK.view.showFollowers,person); //could also use slider for this
			}
			else
			{//already got my followers in local db
				if(typeof TK.db.followers[0] == 'object')
				{//get followers from db
					TK.view.showFollowers(TK.db.followers);
				}
				else
				{
					TK.model.findFollowers(TK.view.showFollowers);
				}
			}
		},

		/**
		* grab the secret personal information
		* @param {String} person - the persons nickname
		*/
		showDetails: function(person){
			widget.log('Start: controller.showDetails');

			if(typeof person == 'string')
			{//got someone else to lookup
                var current_tab = $$('#'+widget.body.id+' div.nv-tabSet li.selected');
                current_tab = current_tab[0].getProperty('name');
				var div = $$('#'+widget.body.id+' div.nv-tabContent[name='+current_tab+'] div.c_wrap');
                TK.controller.detailSlider(div[0], person);
			}
			else
			{// find my information when tab 5 -> 'Me' is clicked
				TK.model.findDetails(TK.view.showDetails);
			}
		},
		
		/**
		 * the postings page
		 */
		showForm: function () {
			widget.log('Start: controller.showForm');
			//get current div
			var div = $$('#'+widget.body.id+' div.nv-tabContent[name=tab20]');
			var content = TK.view._partialShowForm(TK.db.friends);

			TK.db.tabs.setContent("tab20",content);
			//append those funky events to form
			TK.controller._watchForm(div[0]);
			TK.controller._showCounter(div[0]);			
		},

        /**
        * Slides in the details for a selected person and slides the old listing content back in
        * is used for friends and followers
        * var div = $$('#'+widget.body.id+' div.nv-tabContent[name=tab40] div.c_wrap');
            div = div[0];
        * @param {Object} div - a DOM Object
        *                       var div = $$('#'+widget.body.id+' div.nv-tabContent[name=tab40] div.c_wrap');
        *                       div = div[0];
        * @param {String} person
        */
        detailSlider: function (div, person) {
            widget.log('Start: controller.detailSlider');

            var mySlider = new Fx.Slide(div, {duration: 500, mode: 'horizontal'});
            //slide tab content out
            mySlider.slideOut ();
            //keep old content
            var old_content = div.innerHTML;

            //data is not cached like my friends are
            TK.model.findDetails(function (data) {
                var content = TK.view._partialPersonDetails(data);
                //slide new content in
                new_content = '<a href="#" class="go_back"> << back to list</a>'; //backlink
                new_content += content;
                div.setHTML(new_content);
                mySlider.slideIn();

                div.getElement('a.go_back').onclick = function (e) {
                    e.preventDefault();
                    //backlink is clicked
                    //slide out current content
                     mySlider.slideOut();
                     //set new content + link events
                     (function() {div.setHTML(old_content); TK.controller._watchPersonLinks();}).delay(700);

                     //slide in after a second
                     (function() { mySlider.slideIn(); }).delay(700);
                };

            },person);
        },
        /**
        * Slides in the details for a selected person and slides the old listing content back in
        * is used for friends and followers
        * var div = $$('#'+widget.body.id+' div.nv-tabContent[name=tab40] div.c_wrap');
            div = div[0];
        * @param {Object} div - a DOM Object
        *                       var div = $$('#'+widget.body.id+' div.nv-tabContent[name=tab40] div.c_wrap');
        *                       div = div[0];
        * @param {String} person - the person to send the message to
        * @param {Object} friends - TK.db.friends - json containing my lookedup friends
        */
        formSlider: function (div, person, friends) {
            widget.log('Start: controller.formSlider');

            var mySlider = new Fx.Slide(div, {duration: 500, mode: 'horizontal'});
            //slide tab content out
            mySlider.slideOut ();
            //keep old content
            var old_content = div.innerHTML;

			var content = TK.view._partialShowForm(friends,person);
			
			(function() { 
				//set new content with dirty delay instead of callback
	            new_content = '<a href="#" class="go_back"> << back to list</a>'; //backlink
	            new_content += content;
	            div.setHTML(new_content);
				TK.controller._watchForm(div);//append form events				
				
				//backlink is clicked
				div.getElement('a.go_back').onclick = function (e) {
                    e.preventDefault();                    
                    //slide out current content
                     mySlider.slideOut();
                     //set new content + link events
                     (function() {div.setHTML(old_content); TK.controller._watchPersonLinks();}).delay(700);

                     //slide in after a second
                     (function() { mySlider.slideIn(); }).delay(700);
                };			

			}).delay(700);
            
			//TK.controller._showCounter();
			(function() { mySlider.slideIn(); }).delay(700);
			//append form events
		},
		
		/**
		* after clicking the private message link in the persons / followers list
		* @param {String} person - the persons nickname is used to set the dropdownlist selected above the from
		**/
		sendPrivateMessage: function (person) {
			widget.log('Start: controller.sendPrivateMessage');
			//set active tab to write tab					
			var current_tab = $$('#'+widget.body.id+' div.nv-tabSet li.selected');
            current_tab = current_tab[0].getProperty('name');
			var div = $$('#' + widget.body.id + ' div.nv-tabContent[name=' + current_tab + '] div.c_wrap');
            TK.controller.formSlider(div[0], person, TK.db.friends);
		},
		/**
		 * after submiting a new message clear textarea, recall timeline and go to first tab
		 * @param {Object} data - the json returned from twitter after posting a new msg
		 * @param {Object} div - the DOM Object holding the current form .. from which it was send
		 */
		afterSubmit: function (data, div) {
			widget.log('Start: controller.afterSubmit');
			div.getElement('textarea').removeAttribute('disabled');
			TK.model.findTimeline();
			//go back to read tab
			TK.db.tabs.selectTab('tab10');
		},

		/**
		* recieves the messages from the findTimeline request in the controller
		* saves the last message id in the db and renders the timeline view
		* @param {Object} data - JSON object containing messages or error
        * @param {String} type - the type of messages fetched
		**/
		afterFindTimeline: function (data, type) {
			widget.log('Start: controller.afterFindTimeline');
			var sound_html = '';
			var last_msg_id = TK.db.lastMessage[type];

			if(data[0])
			{//recieved a message
                if(last_msg_id != data[0].id )
				{//get sound embed html if theres a new msg
					sound_html = TK.controller._getMp3Player();
				}

				if(type == 'direct_messages')
				{//special view for direct messages .. uuu so private that they have a different format
					TK.view.showDirectMessages(data, sound_html);
				}
				else
				{//default timeline
					TK.view.showTimeline(data,sound_html);
				}
				//push first msg id onto stack to check later if there are new messages
				last_msg_id = data[0].id;			
			}
            else if(data.error)
            {//sorry mr. king twitter warns you
                TK.view.showError('Twitter says: <br />' + data.error, 'tab10');
            }
            else
			{//sorry mr. king no one likes you
				TK.view.showError('Your '+type+' timeline seems to contain no messages. Are you worried?', 'tab10');
			}
		},

		/**
		 * Do something if the active tab is changing
		 * @param {Object} name - name of the tab
		 * @param {Object} data - aditional data if needed later
		 */
		onActiveTabChange: function (name,data) {

			var tabs = TK.db.tabs;
			//first send the content to hell
			tabs.setContent(name,' ');
			switch(name){
				case 'tab10':
					//find timeline by the current name(key attribute) of the tabDropdown
					var type = tabs.getTab(name).getAttribute('key');
					TK.model.findTimeline(type);

                    //init update checker
					//var seconds = widget.getValue('refresh_time'); //get secons in 10 sec steps
//					var z = setInterval('TK.controller.updateCheck(type)', seconds * 100);
					break;
				case 'tab20':
					//render form with friends from db for the dropdown
					TK.controller.showForm();					
					break;
				case 'tab30':
					TK.db.lookedUpPersons = new Array();//reset the lookup counter
					TK.controller.showFriends();
					break;
				case 'tab40':
					TK.db.lookedUpPersons = new Array();
					TK.controller.showFollowers();
					break;
				case 'tab50':
					TK.db.lookedUpPersons = new Array();
					TK.controller.showDetails();
					break;
				case 'tab60':
					TK.view.showHelp();
					break;
			}
			// Save active tab
			widget.setValue("activeTab", name);
		},
		/**
		*	 look for new twitters depending on the refresh time the user set
		*/
		updateCheck: function (type) {
			//only do the autoupdate if in timeline tab
			widget.log('update checker for'+type);
			//check for updates for the current selected timeline
//			TK.model.findUpdate('private_messages');
		//	TK.model.findUpdate(type);
			//check for private msg

			//if time is reached call this tabs on change event
		},
		/**
		* do stuff after the update check
		*/
		afterFindUpdate: function(type){
			widget.log('update checker');
			//compare the msg id with the msg id in the db lastMessagestack
            var last_msg_id = TK.db.lastMessage[type];

			if(last_msg_id != data[0].id)
			{// hey got something new so send the herold to tell the king
				//trigger current
				TK.controller.onActiveTabChange('tab10');
				//var type = tabs.getTab(name).getAttribute('key');
			}
			//if not in there rerender current tab
		},
			
		/**
		 * controller show counter
		 * has an eye on the message textarea and gives visual feedback
		 * about the input length
		 * @param {Object} div - the current DOM object where the div is located
		 */
		_showCounter: function (div) {
 			widget.log('Start: controller._showCounter');			

			var max = 140;
			var textarea = div.getElement('textarea');
			var textLength = textarea.value.length;		

			if (textLength == 0)
			{//empty textarea
				TK.view.showCounter(max, div);
			}
			else
			{//something in here				
				TK.view.showCounter(max - textLength, div);							
			}			
		},
		
		/**
		 * append submit and click events onto the form
		 * @param {Object} div - the dom element holding the form to watch
		 */
		_watchForm: function (div) {	
			widget.log('Start: controller._watchForm');		
			//bind submit event
			div.getElement('form').onsubmit = function (e) {
				e.preventDefault();
				TK.model.submitStatus(div);					
			};
			var textarea = div.getElement('textarea');
			textarea.value = ''; // set it to null
			TK.view.showCounter(140, div); //init counter		
			textarea.addEvent('keyup',function () { TK.controller._showCounter(div); } );
			textarea.addEvent('keydown',function () { TK.controller._showCounter(div); } );
		},
		/**
		* append events to the persons list links -> show details / show friends ..
		*/
		_watchPersonLinks: function(){
			widget.log('Start controller._watchPersonLinks');
			widget.elements['people_links'] = widget.body.getElementsByClassName('actions');

			//widget.log('found '+widget.elements['people_links'].length+' of your freakin friends' );

			for(var i = 0; i < widget.elements['people_links'].length; i++){
				TK.controller._appendPersonLinkEvents(widget.elements['people_links'][i]);
			}
		},
		/**
		* append the click event to each persons links
		* @param {String} person - the p elemtent from the person list
		*							holding the information of a single person
		*/
		_appendPersonLinkEvents: function(person_actions){
			//widget.log('Start controller._appendPersonLinks');

			//get all links from person
			var links = person_actions.getElementsByTagName("a");
			var person = links[0].getAttribute('href');

            links[0].onclick = function(e){
            	//private talk from girl to boy
				e.preventDefault();
				TK.controller.sendPrivateMessage(person);
				};
			links[1].onclick = function(e){
            	//her hidden details
				e.preventDefault();
				TK.controller.showDetails(person);
				};
            links[2].onclick = function(e){
            	//her stupid friends
				e.preventDefault();
				TK.controller.showFriends(person);
				};
			if(links[3])
			{//got add / destroy friendship links 
				links[3].onclick = function(e){
            		//add as new friend
					e.preventDefault();
					var user_nickname = links[3].getAttribute('href');
					//add buddy .. needs to reload the the cached friends list array
					TK.model.addFriend(TK.view.showFriends,user_nickname);
					};
			}
			return;
		},
		
		_appendPersonDetailsLinkEvents: function () {
			
			//widget.elements['people_links'] = [];
			//widget.elements['people_links'] = widget.body.getElementsByClassName('actions');
			//widget.log(widget.elements['people_links']);
			/*
			var td = widget.body.getElementsByClassName('person_actions');
			widget.log(td);
			
			var links = widget.elements['people_links'].getElementsByTagName("a");
			for(var i = 0; i < links.length; i++){
				
				var link = links[i].getElementsByTagName("a");
				var person = link[0].getAttribute('href');
				
				widget.log(person);
			}
			*/
		},

		/**
		 * some helpers to change behaviors when widget is iframed
		 * - set bg img for tabsDropdown
		 */
		_standaloneCompat: function(){
			//find all dropdown tabs			
			widget.elements['DropTabs']	= widget.body.getElementsByClassName('placeHolder' );
			//what did i find??
			//widget.log(widget.elements['DropTabs']);
			//replace /img/s.gif with full reference
			for(var i = 0; i < widget.elements['DropTabs'].length; i++) {
				widget.elements['DropTabs'][i].setAttribute('src', 'http://www.netvibes.com/img/s.gif');
			}
		},
		
		/**
		* set the dropdown value according to the current settings
		* @param {String} tab_name - the tab to lookup the dropdown text for.
		*							this name is used to access the db.dropTabs object so please stay consisent
		*/
		_setTabDropdownText: function(tab_name){
			widget.log('Start controller._setTabDropdownText: ');
					
			var tab_dropdown_text 	= [];
			var ar_index			= null;
			var already_in			= null;

			//later need set tab_name dynamic instead of plain hardwired tab10
			var drop_names = TK.db.dropTabs.tab10.name;
			var drop_types = TK.db.dropTabs.tab10.type;
			//get the type defined in settings
			var timeline_type = widget.getValue('timeline_type');

			if( (timeline_type != ' ') || (timeline_type != null) )
			{// she already made her choice via settings.. somehow not really working in standalone
                //lookup ar index of the current text
				var ar_index = drop_types.indexOf(timeline_type);
				//she wanted this text in first place
				tab_dropdown_text[0] = {text: drop_names[ar_index]};
				already_in			 = drop_names[ar_index].text;
			}

			//set the rest of the tab texts
			for(var i=0;  i < drop_names.length; i++ ){
				if(already_in !== drop_names[i]){
					tab_dropdown_text.push( {text: drop_names[i]} );
				}
			}
			//widget.log('Tab Menu Text is: '+tab_dropdown_text);
			return tab_dropdown_text;
		},
		/**
		 * render the flash embed for those crazy mp3 soundz ... yeah rock´n roll
		 *
		 */
		_getMp3Player: function(){
			widget.log('Start: controller._getMp3Player');
			// build the player HTML
			var sound_file	= TK.model.getSound();
			if(sound_file)
			{
                var playerpath	= 'http://www.my-widget.com/flash/singlemp3player.swf';
				var path		= 'http://www.my-widget.com/soundz/';
				var filename	= path + sound_file;
				var mp3html = '<object';
				mp3html += 'width="1" height="1" ';
				mp3html += 'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">';
				mp3html += '<param name="movie" value="'+playerpath+'?';
				mp3html += 'showDownload=false&file=' + filename + '&autoStart=true';
				mp3html += '&backColor=ffffff&frontColor=ffffff';
				mp3html += '&repeatPlay=false&songVolume=50" />';
				mp3html += '<param name="wmode" value="transparent" />';
				mp3html += '<embed wmode="transparent" width="1" height="1" ';
				mp3html += 'src="' + playerpath + '?'
				mp3html += 'showDownload=false&file=' + filename + '&autoStart=true';
				mp3html += '&backColor=ffffff&frontColor=ffffff';
				mp3html += '&repeatPlay=false&songVolume=50" ';
				mp3html += 'type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />';
				mp3html += '</object>';
				return mp3html;
			}
			return '';
		}

	},  // end controller

	/*****************************************
	 * The View
	 * View: DOM, CSS modifications, display content
	 *****************************************/
	view: {

		/**
		 * display messages expects the data as valid json object!
		 * @param {JSON} data - data returned by twitter after a timeline call
		 */
		showTimeline: function(data,sound_html) {
			widget.log('Start: view.showTimeline');
			widget.log(data.length+' incoming JSON Objects' );

			TK.db.tabs.setContent("tab10", ' ' );

			var content = '<div class="nv-richList">';
			content 	+= sound_html;

			var j = 0;
			for(var i = 0; i < data.length; i++)
			{// for each of the messages

				//output vars init
				var msg			= data[i].text;
				var created		= data[i].created_at;
				var css_class	= '';
				var username	= data[i].user.name;
				var nick_name	= data[i].user.screen_name;
				var user_url	= data[i].user.url;
				var user_image	= data[i].user.profile_image_url;

				//convert urls in link inside message
				var urlRegex = /\b(((https?|ftp|irc|telnet|nntp|gopher|file):\/\/|(mailto|news|data):)[^\s\"<>\{\}\'\(\)]*)/gi;
				//widget.log('Found Link: '+msg.match(urlRegex));
				msg = msg.replace(urlRegex,"<a href='$1'>$1</a>");

				(i%2 == 0) ? css_class='item even' :css_class='item odd' ;

				content +='<div class="'+css_class+'">'+
								'<p class="description"  title="By '+nick_name+' / '+username+' posted on '+created+'">'+
									'<a href="'+user_url+'">'+
									'<img align="left" width="32" height="32" src="' + user_image + '" />'+
									'</a>'+msg+
								'</p>'+
							'</div>';
				j++;
				if(j == widget.getValue('limit') ) break;
			}
			TK.db.tabs.setContent("tab10", content );

		},//end showTimeline

		/**
		* direct messages have a different json format so we cant use the showTimeline
		*/
		showDirectMessages: function(data){
            widget.log('Start: view.showDirectMessages');

			var content = '<div class="nv-richList">';
			var j = 0;
			for(var i = 0; i < data.length; i++)
			{// for each of the messages
				var msg			= data[i].text;
				var created		= data[i].created_at;
				var css_class	= '';
				var from_user	= data[i].sender_screen_name;
				var to_user		= data[i].recipient_screen_name;
				//convert urls in link inside message
				var urlRegex = /\b(((https?|ftp|irc|telnet|nntp|gopher|file):\/\/|(mailto|news|data):)[^\s\"<>\{\}\'\(\)]*)/gi;
				//widget.log('Found Link: '+msg.match(urlRegex));
				msg = msg.replace(urlRegex,"<a href='$1'>$1</a>");

				(i%2 == 0) ? css_class='item even' :css_class='item odd' ;

				content +='<div class="'+css_class+'">'+
								'<p class="description"  title="posted on '+created+'">'+
									'<strong>From: '+from_user+'</strong>'+
									'<br/>'+msg+
								'</p>'+
							'</div>';
				j++;
				if(j == widget.getValue('limit') ) break;
			}
			TK.db.tabs.setContent("tab10", content );
		},
		/**
		 * View to show my followers detail
		 * @param {Object} data - json object from twitter call
		 */
		showFollowers: function(data){
			widget.log('Start: view.showFollowers');
			var content ='<div class="c_wrap">';
			for(var i = 0; i < data.length; i++)
			{// for each of the peoples render partial
				content += TK.view._partialPersonList(data[i],i,'followers');
			}
			
			content +='</div>';
			TK.db.tabs.setContent("tab40", content );

			TK.controller._watchPersonLinks();

		},
		/**
		 * View to show my friends detail
		 * @param {Object} data - json object from twitter call
		 */
		showFriends: function(data){
			widget.log('Start: view.showFriends');
			var content ='<div class="c_wrap">';
			var j =0;
			if(TK.db.lookedUpPersons[0] != undefined)
			{//lookup friends of my friends
				content += '<h2>';
                for(var i = 0; i < TK.db.lookedUpPersons.length; i++){
                	if(j==0){ //first
						content += 'Me > '+TK.db.lookedUpPersons[i];
                	}else{
						content += ' > '+TK.db.lookedUpPersons[i];
                	}
					j++
				}
				content += '</h2>';
			}

			for(var i = 0; i < data.length; i++)
			{// for each of the messages
				content += TK.view._partialPersonList(data[i],i);
			}
            content +='</div>';
			TK.db.tabs.setContent("tab30", content );
			TK.controller._watchPersonLinks();

		},
		/**
		 * show information about me
		 */
		showDetails: function (data){
			widget.log('Start: view.showDetails');
			var content = TK.view._partialPersonList(data);
			TK.db.tabs.setContent("tab50", content );
			TK.controller._appendPersonDetailsLinkEvents();
		},

        /**
         * show detailed information about a user
         */
        _partialPersonDetails: function (data) {
            widget.log('Start: view._partialPersonDetails');

            var content = '<table class="nv-datagrid">'+
                            '<tbody>'+
                            '<tr>'+
                                '<th>Link</th>'+
                                '<td>'+
                                '<a href="'+data.url+'" title="'+data.url+'">'+
                                    '<img align="left" width="32" height="32" src="' + data.profile_image_url + '" />'+
                                '</a>'+
                                '</td>'+
                            '</tr><tr>'+
                                '<th>Name</th>'+
                                '<td>'+data.name+'</td>'+
                            '</tr><tr>'+
                                '<th>Nick</th>'+
                                '<td>'+data.screen_name+'</td>'+
                            '</tr><tr>'+
                                '<th>Location</th>'+
                                '<td>'+data.location+'</td>'+
                            '</tr><tr>'+
                                '<th>Protected</th>'+
                                '<td>'+data.protected+'</td>'+
                            '</tr><tr>'+
                                '<th>Friends</th>'+
                                '<td>'+data.friends_count+'</td>'+
                            '</tr><tr>'+
                                '<th>Followers</th>'+
                                '<td>'+data.followers_count+'</td>'+
                            '</tr><tr>'+
                                '<th>Favorites</th>'+
                                '<td>'+data.favourites_count+'</td>'+
                            '</tr><tr>'+
                                '<th>Posts</th>'+
                                '<td>'+data.statuses_count+'</td>'+
                            '</tr><tr>'+
                                '<th>Actions</th>'+
                                '<td class="person_actions">';
            content +=' <a href="'+data.screen_name+'" title="Show Public Messages by this User">Show Msg</a>';

            if(TK.db.friends_ids.indexOf(data.id) == -1 )
            {//this person is not already my friend show follow link
                content +=' | <a href="'+data.screen_name+'" title="Be my shiny new friend">Add</a>';
            }
            else
            {//is friend so allow delete
                content +=' | <a href="'+data.screen_name+'" title="You are not my Friend anymore">[Del]</a>';
            }
            content += '</td></tr></tbody></table>';
            return content;
        },

		
		/**
		 * view the help text
		 */
		showHelp: function(data){

			var content= '<h3>Help</h3>';
			content += '<p>Currently you need to enter your Twitter Password every time you reopen the netvibes page.</p>';
			content += '<h3>To Do</h3>';
			content += '<ul>';
			content += '<li>make better timing of soundz</li>';
			content += '<li>feed the content of this tab remote via rss</li>';
			content += '<li>implement paging for persons lists </li>';
			content += '</ul>';
			content += '<h3>Version</h3>';
			content += '<p>Twitter King Widget '+widget.getValue('version')+'</p>';
			content += '<p>by Georg Leciejewski <a href="http://www.blog.mediaprojekte.de">www.blog.mediaprojekte.de</a></p>';
			//return content;
			TK.db.tabs.setContent("tab60",content);

		},
		/**
		 * view the counter
		 * @param {int} data - the characters left to type
		 */
		showCounter: function (data, div) {
			widget.log('Start: view.showCounter');
			//widget.log('Left Characters: '+data);
			var max = 140;
			if(data < 40 && data > 0)
			{//put the redlights on
				div.getElement('small').setHTML('<span style="color:red;">Carefully choose your remaining '+data+' characters!</span>');
				div.getElement('textarea').setStyle('background','');
			}
			else if(data === 0)
			{//no more remaining chars
				div.getElement('small').setHTML('<span style="color:green;">Nailed that Twitter Limit down!</span>');
				div.getElement('textarea').setStyle('background','#BCF29C');
			}
			else if(data < 0)
			{//to many chars
				div.getElement('small').setHTML('<span style="color:red;">Watch it Babe! Twitter will cut it. Remove '+(data)+' characters!</span>');
				div.getElement('textarea').setStyle('background','#F7D4BC');
			}
			else
			{//still enough room in here
				div.getElement('small').setText(+data+' Characters left');
				div.getElement('textarea').setStyle('background','');
			}
		},
		/**
		 * if the controller found some kind of error it will be rendered in the given tab
		 * all content inside the tab is reset with the message
		 * @param {string} data - the error message
		 * @param {string} tab  - name of the tab to show the error on
		 */
		showError: function (data, tab) {
			widget.log('Start: view.showError');
			TK.db.tabs.setContent(tab,'<div class="error">'+data+'</div>');

		},
		
		/**
		 * view the form to post new message
		 * @param {Object} data - the persons needed to make this thing really private
		 * @param {String} person - the queen you want to get private with
		 */
		_partialShowForm: function (data,person) {
			widget.log('Start view._partialShowForm');
			var content='<form method="post" action="http://twitter.com/statuses/update.json">';
			content += TK.view._partialPersonDropdown(data,'person_dropdown',person);
			content += 	'<small>Counter</small><br/>'+
						'<textarea rows="2" name="status"></textarea><br/>'+
						'<input type="submit" class="submit" value="Update" />'+
					'</form>';
				
			return content;
		},
		/**
		 * template "partial" for showing the list of friends or followers
		 * @param {Object} data - json object holding one persons information
		 * @param {Int} count - the array counter to render odd/even styles
		 */
		_partialPersonList: function (data,count,show_what) {
			//widget.log('Start: view._partialPersonList');

				//var msg			= data.status.text;
				//var date		= data.status.created_at;
				var css_class	= '';
				var username	= data.name;
				var user_id		= data.id;
				var description	= data.description;
				var nick_name	= data.screen_name;
				var user_url	= data.url;
				var user_image	= data.profile_image_url;
				var location	= data.location;

				if(description === null){
					description = '';
				};

				(count%2 == 0) ? css_class='item even' : css_class='item odd' ;

				var content ='<div class="'+css_class+'">'+
								'<p class="description">'+
									'<a href="'+user_url+'" title="go to '+user_url+'">'+
										'<img align="right" width="45" height="45" src="' + user_image + '" />'+
									'</a>'+
									'<h3 class="tk_h3">'+nick_name+' / '+username+' / '+location+'</h3>'+
									description+
									'<br/><br/><span class="actions">'+
									'<span class="hidden_details">'+nick_name+'</span>'+
									'<a href="'+nick_name+'">Private Msg</a> | '+
									'<a href="">Details</a> | '+
									'<a href="">Friends</a> ';
				if(show_what =='followers')
				{// only for followers

					if(TK.db.friends_ids.indexOf(user_id) == -1 )
					{//this person is not already my friend show follow link
						content +='| <a href="'+nick_name+'" title="Add as new friend">[+]</a>';
					}
				}

				content +='</span></p>'+
					'</div>';
				return content;
		},
		/**
		 * show all my friends in a dropdown used for private messages and more later
		 * @param {Object} data 	 - all my cool twitter friends
		 * @param {String} css_class - css class used for the select, so it can be uniquely selected via getByClassName
		 * @param {String} css_class - the selected queen
		 */
		_partialPersonDropdown: function(data,css_class,person){
			//widget.log('Start: view._partialPersonDropdown');

			var content ='<select name="friends" class="'+css_class+'">';
			content 	+=  '<option value="">Make it private with:</option>'; // not what you think
			for(var i = 0; i < data.length; i++)
			{// for each of the girls
				var username	= data[i].name;
				var nick_name	= data[i].screen_name;

				if( (person != undefined) && (person == nick_name) ){
					content +=  '<option value="'+nick_name+'" selected="selected">'+username+'</option>';
				}else{
					content +=  '<option value="'+nick_name+'">'+username+'</option>';
				}
			}
			content +='</select>';
			return content;
		}

	}// end view

};

/***************************
 * native functions
 **************************/

widget.onLoad = function() {

	widget.log('TwitterKing.onLoad');


	if (typeof Netvibes.UI._idIncrement == "undefined") {
		Netvibes.UI._idIncrement = 0;
	}

	//show something
	widget.setTitle('Twitter King is loading..');
	if( widget.getValue('timeline_type'))
	{
		TK.controller.index();
	}
	else
	{
		widget.setBody('<div class="error">Please edit and save your settings.</div><div class="error">This Widget currently does not work in standalone mode.</div>');
	}
}

widget.onRefresh = function() {
	widget.setBody( _("Loading ... Twitter Junkie") );
	widget.onLoad();
}

</script>
<!-- end controller-->

<!-- start style-->
	<link rel="stylesheet" type="text/css" href="http://www.netvibes.com/themes/uwa/style.css" />
	<style type="text/css">
	/* your CSS rules */
	.tk_h3{color:#FF6600;font-size:11px}
	.tk_small{float:right;margin:0.25em 0 0 0;}
	.nv-tabContent{min-height:150px;}
		.nv-tabContent textarea{width:100%;height:80px; clear:both}
		.nv-tabContent form small{float:right; clear:both;}

	.content{float:left;clear:right}
	.item{padding:4px}
		.description{clear:both; }
			.description img{padding:0 5px 0 0}
	.hidden_details{display:none}
	.error{text-align: center; background: #FFE0E1; font-weight: bold;padding:4px;}
	</style>
<!-- end style-->

</head>
<!-- end header-->

<body>
<p>Twitter along John .. </p>

</body>
</html>
